package com.cci.kangdao.utilTool;

import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.ser.BeanPropertyWriter;
import com.fasterxml.jackson.databind.ser.BeanSerializerModifier;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.json.JSONArray;
import org.json.JSONException;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;

/**
 * @author William Du
 * @date 2019/3/20
 */
public class JsonUtils {

    public static Boolean isJsonString(String jsonStr) {
        if (StringUtils.isEmpty(jsonStr)) {
            return false;
        }

        ObjectMapper mapper = new ObjectMapper();
        try {
            mapper.readValue(jsonStr, Object.class);
        } catch (Exception e) {
            return false;
        }
        return true;

    }

    public static String toJsonString(Object obj) {
        String jsonStr;
        if (obj == null) {
            return null;
        }

        ObjectMapper mapper = new ObjectMapper();
        try {
            jsonStr = mapper.writeValueAsString(obj);
        } catch (IOException e) {
            throw new IllegalArgumentException(e);
        }
        return jsonStr;
    }

    public static String toJsonStringFilterEmpty(Object obj) {
        String jsonStr;
        if (obj == null) {
            return null;
        }

        ObjectMapper mapper = new ObjectMapper();
        try {
            mapper.setSerializerFactory(mapper.getSerializerFactory().withSerializerModifier(new NullValueSerializerModifier()));
            jsonStr = mapper.writeValueAsString(obj);
        } catch (IOException e) {
            throw new IllegalArgumentException(e);
        }
        return jsonStr;
    }

    public static String toNonNullJsonString(Object obj) {
        String jsonStr;
        if (obj == null) {
            return null;
        }
        ObjectMapper mapper = new ObjectMapper();
        mapper.setSerializationInclusion(Include.NON_NULL);
        try {
            jsonStr = mapper.writeValueAsString(obj);
        } catch (IOException e) {
            throw new IllegalArgumentException(e);
        }
        return jsonStr;
    }

    public static <T> T toJavaObject(String jsonStr, Class<T> clazz) {
        T obj;
        if (StringUtils.isEmpty(jsonStr)) {
            return null;
        }
        ObjectMapper mapper = new ObjectMapper();
        mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
        //mapper.enable(DeserializationFeature.ACCEPT_EMPTY_ARRAY_AS_NULL_OBJECT);
        try {
            obj = mapper.readValue(jsonStr, clazz);
        } catch (IOException e) {
            throw new IllegalArgumentException(e);
        }
        return obj;
    }

    public static <T> List<T> convertJsonArrayToList(String jsonStr, Class<T> clazz) {
        List<T> list = new ArrayList<>();
        try {
            if (!StringUtils.isEmpty(jsonStr) && JsonUtils.isJsonString(jsonStr)) {
                JSONArray jsonArray = new JSONArray(jsonStr);
                list = convertJsonArrayToList(jsonArray, clazz);
            }
        } catch (JSONException e) {
            e.printStackTrace();
        }
        return list;
    }

    public static <T> List<T> convertJsonArrayToList(JSONArray jsonArray, Class<T> clazz) {
        List<T> list = new ArrayList<>();
        try {
            for (int i = 0; i < jsonArray.length(); i++) {
                String json = jsonArray.get(i).toString();
                T obj = JsonUtils.toJavaObject(json, clazz);
                list.add(obj);
            }
        } catch (JSONException e) {
            e.printStackTrace();
        }
        return list;
    }

    public static String pickSub(String json, String key) {
        Map<String, Object> map = toJavaObject(json, Map.class);
        if (MapUtils.isNotEmpty(map)) {
            for (Map.Entry<String, Object> sub : map.entrySet()) {
                if (sub.getKey().equals(key)) {
                    //包含final，直接返回值
                    if ((sub.getValue().getClass().getModifiers() & 16) == 16) {
                        return sub.getValue().toString();
                    }
                    // 否则返回json字符串
                    return toJsonString(sub.getValue());
                }
            }
        }
        return null;
    }

    /**
     * 处理数组等类型的null值
     */
    public static class NullArrayJsonSerializer extends JsonSerializer<Object> {

        @Override
        public void serialize(Object value, JsonGenerator jsonGenerator, SerializerProvider provider) throws IOException {
            if (value == null) {
                jsonGenerator.writeStartArray();
                jsonGenerator.writeEndArray();
            }
        }
    }


    /**
     * 处理字符串等类型的null值
     */
    public static class NullStringJsonSerializer extends JsonSerializer<Object> {

        @Override
        public void serialize(Object o, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
            jsonGenerator.writeString(StringUtils.EMPTY);
        }
    }

    /**
     * 处理数字等类型的null值
     */
    public static class NullNumberJsonSerializer extends JsonSerializer<Object> {

        @Override
        public void serialize(Object o, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
            jsonGenerator.writeNumber(0);
        }
    }

    /**
     * 处理布尔类型的null值
     */
    public static class NullBooleanJsonSerializer extends JsonSerializer<Object> {
        @Override
        public void serialize(Object o, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
            jsonGenerator.writeBoolean(false);
        }
    }


    public static class NullValueSerializerModifier extends BeanSerializerModifier {
        @Override
        public List<BeanPropertyWriter> changeProperties(SerializationConfig config, BeanDescription beanDesc, List<BeanPropertyWriter> beanProperties) {
            //循环所有的beanPropertyWriter
            for (Object beanProperty : beanProperties) {
                BeanPropertyWriter writer = (BeanPropertyWriter) beanProperty;
                //判断字段的类型，如果是array，list，set则注册nullSerializer
                if (isArrayType(writer)) {
                    //给writer注册一个自己的nullSerializer
                    writer.assignNullSerializer(new NullArrayJsonSerializer());
                } else if (isNumberType(writer)) {
                    writer.assignNullSerializer(new NullNumberJsonSerializer());
                } else if (isBooleanType(writer)) {
                    writer.assignNullSerializer(new NullBooleanJsonSerializer());
                } else if (isStringType(writer)) {
                    writer.assignNullSerializer(new NullStringJsonSerializer());
                } else {
                    writer.assignNullSerializer(new NullStringJsonSerializer());
                }
            }
            return beanProperties;
        }

        /**
         * 是否是数组
         */
        private boolean isArrayType(BeanPropertyWriter writer) {
            Class<?> clazz = writer.getType().getRawClass();
            return clazz.isArray() || Collection.class.isAssignableFrom(clazz);
        }

        /**
         * 是否是string
         */
        private boolean isStringType(BeanPropertyWriter writer) {
            Class<?> clazz = writer.getType().getRawClass();
            return CharSequence.class.isAssignableFrom(clazz) || Character.class.isAssignableFrom(clazz);
        }


        /**
         * 是否是int
         */
        private boolean isNumberType(BeanPropertyWriter writer) {
            Class<?> clazz = writer.getType().getRawClass();
            return Number.class.isAssignableFrom(clazz);
        }

        /**
         * 是否是boolean
         */
        private boolean isBooleanType(BeanPropertyWriter writer) {
            Class<?> clazz = writer.getType().getRawClass();
            return clazz.equals(Boolean.class);
        }

    }
}
